package com.xyzwps.paimon.katheryne.commons.dbmapper;

import com.google.common.base.CaseFormat;
import com.xyzwps.paimon.katheryne.modules.user.UserAccount;
import io.helidon.dbclient.DbRow;
import io.vavr.Function1;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
import java.util.Objects;
import java.util.function.Supplier;

public class RecordMapper<T> {

    private final Class<T> record;

    private final RecordComponent[] properties;

    private final Constructor<T> constructor;

    private final Function1<String, String> toColumnName;

    public RecordMapper(Class<T> record) {
        this.record = Objects.requireNonNull(record);
        if (!this.record.isRecord()) {
            throw new IllegalArgumentException("Not a record: " + record.getCanonicalName());
        }

        var comps = record.getRecordComponents();
        this.properties = comps;

        this.constructor = apply(() -> {
            try {
                var params = new Class<?>[comps.length];
                for (int i = 0; i < comps.length; i++) {
                    params[i] = comps[i].getType();
                }

                return record.getDeclaredConstructor(params);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });

        this.toColumnName = Function1.of((propName) -> CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE).convert(propName));
    }

    private static <T> T apply(Supplier<T> supplier) {
        return supplier.get();
    }

    public T from(DbRow row) {
        final int len = this.properties.length;
        var args = new Object[len];

        for (int i = 0; i < len; i++) {
            var prop = this.properties[i];
            var columnName = toColumnName.apply(prop.getName());
            var column = row.column(columnName);

            var propType = prop.getType();

            if (propType == int.class) {
                args[i] = MapDbColumn.toIntValue(column);
            } else if (propType == long.class) {
                args[i] = MapDbColumn.toLongValue(column);
            } else if (propType == boolean.class) {
                args[i] = MapDbColumn.toBooleanValue(column);
            } else if (propType == String.class) {
                args[i] = MapDbColumn.toString(column);
            } else {
                args[i] = MapDbColumn.to(column, propType);
            }
        }

        try {
            return constructor.newInstance(args);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        new RecordMapper<>(UserAccount.class);
    }


}
